package com.hky.algorithm.mahjongGame;

import com.hky.algorithm.mahjongGame.constent.GangType;
import com.hky.algorithm.realize.Calculator;
import com.hky.algorithm.realize.Game;
import com.hky.algorithm.realize.Player;
import com.hky.algorithm.realize.Type;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * 对局中的玩家
 */
@Slf4j
@AllArgsConstructor
@NoArgsConstructor
public class GamePlayer extends Player {
    // 玩家浏览器连接到服务器的sessionID
    public String sessionId;
    // 玩家名称
    public String name;
    // 对局数据
    public Game game = Game.getInitGame();
    // 碰的牌
    public Set<Card> pengCard = new HashSet<>();
    // 手牌，每张牌对应的数量
    public TreeMap<Card, Integer> cardMap = new TreeMap<>();
    // 用于处理自己手牌中要胡的牌
    public Game selfGame = Game.getInitGame();
    // 胡了的牌
    public Card huCard;
    // 加入房间的次序
    public Integer order;


    public GamePlayer(String sessionId, String name) {
        this.sessionId = sessionId;
        this.name = name;
//        this.cards_num = 13; // 父类牌的数量
    }

    /**
     * 初始化整个GamePlayer
     *
     * @param gameData 对局数据
     */
    public void initGamePlayer(GameData gameData) {
        this.alreadyHu = false;
        this.lack = null;
        this.huCard = null;
        this.cards_num = 0;
        initGame(gameData);
        initSelfGame();
    }

    /**
     * 清理数据
     */
    public void clearGamePlayer() {
        pengCard.clear();
        cardMap.clear();
    }

    /**
     * 初始化玩家危险牌计算数据
     *
     * @param gameData 对局数据
     */
    public void initGame(GameData gameData) {
        game.initTerms(this);
        game.initPlayers(gameData, this);
    }

    /**
     * 初始化用于计算自己要胡的牌的 Game 类
     */
    private void initSelfGame() {
        // 留着后期使用
        selfGame.clear();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        if (!super.equals(o)) return false;
        GamePlayer that = (GamePlayer) o;
        return Objects.equals(sessionId, that.sessionId) && Objects.equals(name, that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), sessionId, name);
    }

    /**
     * 自己摸牌时是否能杠
     *
     * @return size=0，不能杠
     */
    public List<GangItem> canSelfGang() {
        Set<Map.Entry<Card, Integer>> cardEntries = cardMap.entrySet();

        List<GangItem> res = new ArrayList<>();
        for (Map.Entry<Card, Integer> cardEntry : cardEntries) {
            Card card = cardEntry.getKey();
            if (pengCard.contains(card)) res.add(new GangItem(card, GangType.ADD));
            if (cardEntry.getValue() == 4) res.add(new GangItem(card, GangType.DARK));
        }

        return res;
    }

    /**
     * 别人打牌时，查看自己能不能杠或者碰
     * <br/>
     * 能杠的话也可以碰，玩家自己选择
     *
     * @return null 代表都不能
     */
    public GangItem canGangOrPeng(Card card) {
        if (alreadyHu) return null;

        int n = cardMap.getOrDefault(card, 0);

        if (n == 3) return new GangItem(card, GangType.NORMAL);
        if (n == 2) return new GangItem(card, GangType.PENG);
        return null;
    }


    public static void main(String[] args) {
        GamePlayer gamePlayer = new GamePlayer();
//        List<Card> handCards = gamePlayer.handCards;
//        Set<Card> pengCard = gamePlayer.pengCard;
//
//        handCards.add(new Card(1, Type.TIAO));
//        handCards.add(new Card(3, Type.TIAO));
//        handCards.add(new Card(5, Type.TIAO));
//        handCards.add(new Card(8, Type.TIAO));
//
//        handCards.add(new Card(1, Type.WAN));
//        handCards.add(new Card(3, Type.WAN));
//        handCards.add(new Card(5, Type.WAN));
//        handCards.add(new Card(8, Type.WAN));
//
//        gamePlayer.fetchCard(new Card(4, Type.TIAO));
//        gamePlayer.fetchCard(new Card(6, Type.WAN));
//        gamePlayer.fetchCard(new Card(6, Type.TONG));
//        System.out.println(handCards);
    }

    /**
     * 玩家摸了一张牌，将牌插入到手牌中
     *
     * @param card 牌
     */
    public void fetchCard(Card card) {
        // 牌添加到手牌前先计算危险牌，用于发牌时判断是否自摸
        calSelfDanger();
        cardMap.put(card, cardMap.getOrDefault(card, 0) + 1);
        cards_num += 1;
        selfGame.addCardToTerm(card, 1);
        game.subCardToTerm(card, 1);
    }

    /**
     * 出一张牌
     *
     * @param card 出的牌
     * @param num  出的数量（杠或者碰时也会调用此函数）
     */
    public void outCardSelf(Card card, int num) {
        int n = cardMap.get(card) - num;
        if (n == 0) cardMap.remove(card);
        else cardMap.put(card, n);
        cards_num -= num;
        selfGame.subCardToTerm(card, num);
        // 打出去一张牌后计算要胡那些牌
        calSelfDanger();
    }

    /**
     * 杠牌，将杠的牌从手牌中减去
     *
     * @param gangItem 杠的牌
     */
    public void selfGangOrPeng(GangItem gangItem) {
        GangType type = gangItem.type;
        Card card = gangItem.card;
        switch (type) {
            case ADD:
                outCardSelf(card, 1);
                break;
            case DARK:
                outCardSelf(card, 4);
                break;
            case NORMAL:
                outCardSelf(card, 3);
                break;
            default: // 碰
                pengCard.add(card);
                outCardSelf(card, 2);
        }
    }

    /**
     * 对手出牌（打出一张牌、杠、碰）后更新game数据
     *
     * @param card 出的牌
     * @param num  张数，打牌 碰牌 杠牌 = 1 2 3
     */
    public void updateGame(Card card, int num) {
        game.subCardToTerm(card, num);
    }

    /**
     * 计算对手的危险牌
     */
    public void calRivalDanger() {
        Calculator.calculate(game);
    }

    /**
     * 计算自己的危险牌（要胡的牌）
     */
    public void calSelfDanger() {
        Calculator.calculate(selfGame);
    }

    /**
     * 收集危险牌
     *
     * @param game 对局数据
     * @return 危险牌set
     */
    public Set<Card> collectDanger(Game game) {
        return game.collectDanger();
    }


    /**
     * 定缺
     */
    public void defineLack(Type type) {
        lack = type;
        selfGame.players[0].lack = type;
    }

    /**
     * 添加自己到selfGame中的
     */
    public void addSelfToSelfGame() {
        selfGame.players = new Player[1];
        selfGame.players[0] = this;
    }

    /**
     * 添加对手的缺牌数据
     *
     * @param player 对手
     */
    public void defineRivalLack(Player player) {
        for (Player curPlayer : game.players) {
            if (curPlayer.equals(player)) {
                curPlayer.lack = player.lack;
                return;
            }
        }
    }

    /**
     * 判断自己是否能胡card
     *
     * @param card 牌
     */
    public boolean canHu(Card card) {
//        calSelfDanger();
        return !alreadyHu && selfGame.isDangerCard(card);
    }

    /**
     * 对手碰 杠，更新game中term数据
     */
    public void rivalGangOrPeng(GangItem gangItem) {
        GangType type = gangItem.type;
        Card card = gangItem.card;
        switch (type) {
            case ADD:
                game.subCardToTerm(card, 1);
                break;
            case DARK:
                game.subCardToTerm(card, 4);
                break;
            case NORMAL:
                game.subCardToTerm(card, 3);
                break;
            default: // 碰
                game.subCardToTerm(card, 2);
        }
    }
}
